﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OverStore.Scoping.Manager;
using OverStore.Scoping;

namespace OverStore.TutorialAsTest.UnitTest.Transaction
{
    [TestClass]
    public class OperationScopeSimplestTest
    {
        private readonly OperationScopeManager _ScopeManager = new OperationScopeManager();

        [TestMethod]
        public void NestesOperationScopesComplete()
        {
            bool isCompleted = false;
            bool isFailed = false;

            _ScopeManager.ScopeExited += (s, e) =>
            {
                if (e.Action == OperationScopeAction.Completing ||
                    e.Action == OperationScopeAction.Failing)
                {
                    isCompleted = e.Action == OperationScopeAction.Completing;
                    isFailed = e.Action == OperationScopeAction.Failing;
                }
            };

            using (var root = new OperationScope())
            {
                _ScopeManager.StartOperationScope(root);
                using (var scope = new OperationScope())
                {
                    _ScopeManager.StartOperationScope(scope);
                    Assert.IsFalse(isCompleted);
                    Assert.IsFalse(isFailed);
                    scope.Complete();
                    InternalTransactionalMethod(ref isCompleted, ref isFailed);
                    Assert.IsFalse(isCompleted);
                    Assert.IsFalse(isFailed);
                }
                using (var scope = new OperationScope())
                {
                    _ScopeManager.StartOperationScope(scope);
                    Assert.IsFalse(isCompleted);
                    Assert.IsFalse(isFailed);
                }
                Assert.IsFalse(isCompleted);
                Assert.IsFalse(isFailed);
                root.Complete();
                Assert.IsTrue(isCompleted);
                Assert.IsFalse(isFailed);
            }
        }

        private void InternalTransactionalMethod(ref bool isCompleted, ref bool isFailed)
        {
            using (var root = new OperationScope())
            {
                _ScopeManager.StartOperationScope(root);
                root.Complete();
                Assert.IsFalse(isCompleted);
                Assert.IsFalse(isFailed);
            }
            Assert.IsFalse(isCompleted);
            Assert.IsFalse(isFailed);
        }

        [TestMethod]
        public void NestesOperationScopesWithException()
        {
            bool isCompleted = false;
            bool isFailed = false;

            _ScopeManager.ScopeExited += (s, e) =>
            {
                if (e.Action == OperationScopeAction.Completing ||
                       e.Action == OperationScopeAction.Failing)
                {
                    isCompleted = e.Action == OperationScopeAction.Completing;
                    isFailed = e.Action == OperationScopeAction.Failing;
                }
            };

            try
            {
                using (var root = new OperationScope())
                {
                    _ScopeManager.StartOperationScope(root);
                    Assert.IsFalse(isCompleted);
                    Assert.IsFalse(isFailed);
                    if (!isCompleted)
                        throw new InvalidOperationException();
                    root.Complete();
                }
            }
            catch (InvalidOperationException)
            {
            }
            Assert.IsFalse(isCompleted);
            Assert.IsTrue(isFailed);
        }

        [TestMethod]
        public void NestesOperationScopesWithoutUsingWithMissingComplete()
        {
            bool isCompleted = false;
            bool isFailed = false;

            _ScopeManager.ScopeExited += (s, e) =>
            {
                if (e.Action == OperationScopeAction.Completing ||
                       e.Action == OperationScopeAction.Failing)
                {
                    isCompleted = e.Action == OperationScopeAction.Completing;
                    isFailed = e.Action == OperationScopeAction.Failing;
                }
            };

            using (var root = new OperationScope())
            {
                _ScopeManager.StartOperationScope(root);
                var scope = new OperationScope();
                _ScopeManager.StartOperationScope(scope);
                ChaosOperationScopesCreation();
                root.Complete();
                Assert.IsTrue(isCompleted);
                Assert.IsFalse(isFailed);
            }
        }

        private void ChaosOperationScopesCreation()
        {
            var scope1 = new OperationScope();
            var scope2 = new OperationScope();
            _ScopeManager.StartOperationScope(scope1);
            _ScopeManager.StartOperationScope(scope2);
            using (var scope3 = new OperationScope())
            {
                _ScopeManager.StartOperationScope(scope3);
                scope2.Complete();
            }
            var s = new OperationScope();
            _ScopeManager.StartOperationScope(s);
        }
    }
}
